package org.jmc.refmanager.domain.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.jmc.refmanager.domain.dao.ChangeDao;
import org.jmc.refmanager.domain.dao.OrgUnitDao;
import org.jmc.refmanager.domain.dao.QueryParam;
import org.jmc.refmanager.domain.dao.UserDao;
import org.jmc.refmanager.domain.model.Change;
import org.jmc.refmanager.domain.model.Change.Operation;
import org.jmc.refmanager.domain.model.Change.TaskStatus;
import org.jmc.refmanager.domain.model.OrgUnit;
import org.jmc.refmanager.domain.model.User;
import org.jmc.refmanager.domain.service.ChangeManager;
import org.jmc.refmanager.domain.service.UserManager;
import org.jmc.refmanager.domain.service.exception.BusinessException;
import org.jmc.refmanager.security.SecurityHelper;
import org.jmc.refmanager.util.LoggingUtils;
import org.jmc.refmanager.util.Update;
import org.jmc.refmanager.util.UpdatesList;
import org.jmc.refmanager.util.UpdatesList.UpdateType;
import org.jmc.refmanager.util.Util;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("taskManager")
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public class ChangeManagerImpl implements ChangeManager {
	private final static Logger logger = LoggerFactory.getLogger(ChangeManager.class);

	@Resource(name="taskDao")
	private ChangeDao taskDao;

	@Resource(name="userManager")
	private UserManager userManager;

	@Resource(name="userDao")
	private UserDao userDao;

	@Resource(name="orgUnitDao")
	private OrgUnitDao orgUnitDao;

	/**
	 * {@inheritDoc}
	 */
	public List<Change> findAll() {
		try {
			return taskDao.findAll();
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public Change findById(Integer taskId) {
		try {
			return taskDao.find(taskId);
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Transactional(readOnly = false)
	public Change update(Change detachedTask) {
		try {
			return taskDao.merge(detachedTask);
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	/**
	 * {@inheritDoc}
	 * This level of transaction is need to populate join table
	 * in a many to many relation ship
	 */
	@Transactional(readOnly = false, propagation = Propagation.MANDATORY)
	public void persist(Change transientTask) {
		try {
			taskDao.persist(transientTask);
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Transactional(readOnly = false)
	public void remove(Change persistentTask) {
		try {
			taskDao.remove(persistentTask);
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Transactional(readOnly = false)
	public void remove(Integer taskId) {
		try {
			taskDao.remove(taskId);
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	@Override
	@Transactional(readOnly = false)
	public void assignNewService(OrgUnit newService, User user) {
		try {
			OrgUnit parent = newService.getParent();
			LoggingUtils.logDebug(logger, 
					"Recherche de la direction de validation du service '%s'", parent==null?"":String.valueOf(parent.getId()));
			OrgUnit direction = orgUnitDao.getDirectionValidation(parent);
	
			if (direction == null) {
				throw new BusinessException("Impossible de trouver la direction de validation dans la hiérarchie.");
			}
			
			String matricule = user.getMatricule();
			
			// Creation d'un change de type creation
			Change changeCreation = new Change();
			changeCreation.setName("Création du service '" + newService.getLibCourt() 
					+ "' (" + matricule + ")");

			changeCreation.setObjectDescription(newService.getLibCourt());

			String xmlObject = newService.getSerializedBean();
			changeCreation.setObject(xmlObject);
			changeCreation.setStatus(TaskStatus.AVAILABLE);
			changeCreation.setOperation(Operation.CREATE);
			changeCreation.setDirectionId(direction.getId());
			changeCreation.setAuthorName(matricule);

			LoggingUtils.logDebug(logger, "Création du changement [création]: %s", changeCreation.getName());

			// Recherche les approbateurs du noeud parent car le service n'est pas encore persiste
			LoggingUtils.logDebug(logger, "Recherche des référents de direction du service '%s'", parent==null?"":parent.getId());
			Set<User> approvers = userManager.findApproversOfDirection(direction);
			if (Util.isNullOrEmpty(approvers)) {
				throw new BusinessException("Aucun référent de direction n'est défini pour le service '%s'.", parent==null?"":parent.getLibCourt());
			}
			LoggingUtils.logDebug(logger, "Assignation du changement aux référents de direction");
			for (User approver : approvers) {
				approver.addTask(changeCreation);
				// Cet appel sauvegarde aussi le change
				userDao.persist(approver);
			}
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	@Override
	@Transactional(readOnly = false)
	public void assignDeleteService(OrgUnit service, User user) {
		try {
			checkIfServiceHasChanges(service);

			LoggingUtils.logDebug(logger, 
					"Recherche de la direction de validation du service '%s'", service==null?"":String.valueOf(service.getId()));
			OrgUnit direction = orgUnitDao.getDirectionValidation(service);
	
			if (direction == null) {
				throw new BusinessException("Impossible de trouver la direction de validation dans la hiérarchie.");
			}
	
			String matricule = user.getMatricule();
			
			// Creation d'un change de type update
			Change changeUpdate = new Change();
			
			changeUpdate.setName("Suppression du service '" + service.getLibCourt() 
					+ "' (" + user.getMatricule() + ")");
			changeUpdate.setOperation(Operation.DELETE);
			
			changeUpdate.setObjectDescription(service.getLibCourt());
			changeUpdate.setDirectionId(direction.getId());
			changeUpdate.setAuthorName(matricule);

			LoggingUtils.logDebug(logger, "Création du changement [suppression]: %s", changeUpdate.getName());

			String xmlChanges = service.getSerializedChanges();
			changeUpdate.setObject(xmlChanges);
			changeUpdate.setStatus(TaskStatus.AVAILABLE);
	
			// Recherche le service pour le rattacher au changement
			LoggingUtils.logDebug(logger, "Recherche le service pour le rattacher au changement");
			OrgUnit serviceInTask = orgUnitDao.find(service.getId());
			
			Set<Change> changes = new HashSet<Change>();
			changes.add(changeUpdate);
			serviceInTask.setChanges(changes);
			
			// Recherche les approbateurs du service
			LoggingUtils.logDebug(logger, "Recherche des référents de direction du service '%s'", service==null?"":service.getId());
			Set<User> approvers = userManager.findApproversOfDirection(direction);
	
			if (Util.isNullOrEmpty(approvers)) {
				throw new BusinessException("Aucun référent de direction n'est défini pour le service '%s'.", service==null?"":service.getLibCourt());
			}
			LoggingUtils.logDebug(logger, "Assignation du changement aux référents de direction");
			for (User approver : approvers) {
				approver.addTask(changeUpdate);
				// Cet appel sauvegarde aussi le change
				userDao.persist(approver);
			}
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	@Override
	@Transactional(readOnly = false)
	public void assignUpdateService(OrgUnit service, User user) {
		try {
			checkIfServiceHasChanges(service);
			
			LoggingUtils.logDebug(logger, 
					"Recherche de la direction de validation du service '%s'", service==null?"":String.valueOf(service.getId()));
			OrgUnit direction = orgUnitDao.getDirectionValidation(service);
	
			if (direction == null) {
				throw new BusinessException("Impossible de trouver la direction de validation dans la hiérarchie.");
			}
	
			String matricule = user.getMatricule();
			
			// Creation d'un change de type update
			Change changeUpdate = new Change();
			
			UpdatesList updates = service.getUpdates();
			if (updates.containsUpdateOf(UpdateType.PARENT)) {
				changeUpdate.setName("Mise à jour et déplacement du service '" + service.getLibCourt() 
						+ "' (" + matricule + ")");
				changeUpdate.setOperation(Operation.MOVE_UPDATE);
			} else {
				changeUpdate.setName("Mise à jour du service '" + service.getLibCourt() 
						+ "' (" + user.getMatricule() + ")");
				changeUpdate.setOperation(Operation.UPDATE);
			}

			changeUpdate.setObjectDescription(service.getLibCourt());

			changeUpdate.setDirectionId(direction.getId());
			changeUpdate.setAuthorName(matricule);
			
			LoggingUtils.logDebug(logger, "Création du changement [modification]: %s", changeUpdate.getName());
			
			String xmlChanges = service.getSerializedChanges();
			changeUpdate.setObject(xmlChanges);
			changeUpdate.setStatus(TaskStatus.AVAILABLE);
	
			// Recherche le service pour le rattacher au changement
			LoggingUtils.logDebug(logger, "Recherche le service pour le rattacher au changement");
			OrgUnit serviceInTask = orgUnitDao.find(service.getId());
			
			Set<Change> changes = new HashSet<Change>();
			changes.add(changeUpdate);
			serviceInTask.setChanges(changes);
			
			// Recherche les approbateurs du service
			LoggingUtils.logDebug(logger, "Recherche des référents de direction du service '%s'", service==null?"":service.getId());
			Set<User> approvers = userManager.findApproversOfDirection(direction);

			if (Util.isNullOrEmpty(approvers)) {
				throw new BusinessException("Aucun référent de direction n'est défini pour le service '%s'.", service==null?"":service.getLibCourt());
			}
			LoggingUtils.logDebug(logger, "Assignation du changement aux référents de direction");
			for (User approver : approvers) {
				approver.addTask(changeUpdate);
				// Cet appel sauvegarde aussi le change
				userDao.persist(approver);
			}
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	/**
	 * @param service
	 */
	private void checkIfServiceHasChanges(OrgUnit service) {
		LoggingUtils.logDebug(logger, 
				"Vérifier que le service '%s' n'est pas en cours de modification",
				service==null?"":String.valueOf(service.getId()));
		// Ce cas peut se produire en cas de modification decalee dans le temps
		// Le service ne doit pas etre en cours de changement
		Query query = orgUnitDao.createNamedQuery(OrgUnit.COUNT_CHANGE_ON_TASK);
		Integer countChanges = orgUnitDao.count(query, service.getId());
		if (countChanges != null && countChanges != 0) {
			throw new BusinessException("La tâche a déjà été réservée pour un changement par un autre référent.");
		}
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.MANDATORY)
	public void assignMoveService(OrgUnit service, User user) {
		try {
			checkIfServiceHasChanges(service);

			LoggingUtils.logDebug(logger, 
					"Recherche de la direction de validation du service '%s'", service==null?"":String.valueOf(service.getId()));
			OrgUnit direction = orgUnitDao.getDirectionValidation(service);
	
			if (direction == null) {
				throw new BusinessException("Impossible de trouver la direction de validation dans la hiérarchie.");
			}
	
			String matricule = user.getMatricule();
			
			// Creation d'un change de type move
			Change changeMove = new Change();
			
			changeMove.setName("Déplacement du service '" + service.getLibCourt() 
					+ "' (" + matricule + ")");
			changeMove.setOperation(Operation.MOVE);
			
			String xmlChanges = service.getSerializedChanges();
			changeMove.setObject(xmlChanges);
			changeMove.setStatus(TaskStatus.AVAILABLE);
			changeMove.setDirectionId(direction.getId());
			changeMove.setAuthorName(matricule);
			changeMove.setObjectDescription(service.getLibCourt());

			LoggingUtils.logDebug(logger, "Création du changement [déplacement]: %s", changeMove.getName());
			
			// Recherche le service pour le rattacher au changement
			OrgUnit serviceInTask = orgUnitDao.find(service.getId());
			Set<Change> changes = new HashSet<Change>();
			changes.add(changeMove);
			serviceInTask.setChanges(changes);
	
			// Recherche les approbateurs du service
			LoggingUtils.logDebug(logger, "Recherche des référents de direction du service '%s'", service==null?"":service.getId());
			Set<User> approvers = userManager.findApproversOfService(service);
	
			if (Util.isNullOrEmpty(approvers)) {
				throw new BusinessException("Aucun référent de direction n'est défini pour le service '%s'.", service==null?"":service.getLibCourt());
			}
			LoggingUtils.logDebug(logger, "Assignation du changement aux référents de direction");
			for (User approver : approvers) {
				approver.addTask(changeMove);
				// Cet appel sauvegarde aussi le change
				userDao.persist(approver);
			}
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	@Override
	@Transactional(readOnly = false)
	public void checkout(final Change change, User approver) {
		try {
			// Gestion de la concurrence eventuelle sur la reservation de teche
			Change changeUpdate = (Change) taskDao.execute(new JpaCallback<Change>() {
				@Override
				public Change doInJpa(EntityManager em) throws PersistenceException {
					Change changeInDb = em.find(Change.class, change.getId());
					if (changeInDb.getStatus() == TaskStatus.AVAILABLE) {
						em.lock(changeInDb, LockModeType.PESSIMISTIC_WRITE);
					} else {
						String status = changeInDb.getStatus().getLabel();
						if (changeInDb.getStatus() == TaskStatus.RESERVED) {
							String actor = changeInDb.getLockedByUser().getMatricule();
							throw new BusinessException("La tâche n'est pas disponible. Son état est '%s' par '%s'", status, actor);
						} else {
							// TODO Ajouter le nom de l'approbateur dans le schema
							// String approver = changeInDb.getApprover();
							throw new BusinessException("La tâche n'est pas disponible. Son état est '%s'", status);
						}
					}
					return changeInDb;
				}
			});
			
			// T E S T
//			Change changeUpdate = change;
			
			// Positionne le statut du changement
			changeUpdate.setStatus(TaskStatus.RESERVED);
			// Ajoute le nom de l'approbateur
			changeUpdate.setLockedByUser(approver);
			
			taskDao.save(changeUpdate);
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	@Override
	@Transactional(readOnly = false)
	public void commitAndApprove(Change change, OrgUnit updatedServiceByApprover, boolean useApproverUpdates, User approver) {
		try {
			switch (change.getOperation()) {
			case CREATE:
				LoggingUtils.logDebug(logger, "Approbation d'une céation");
				commitNewChange(change, updatedServiceByApprover, useApproverUpdates, approver);
				break;
	
			case DELETE:
				LoggingUtils.logDebug(logger, "Approbation d'une suppression");
				commitDeleteChange(change, approver);
				break;
	
			case MOVE_UPDATE:
			case UPDATE:
				LoggingUtils.logDebug(logger, "Approbation d'une mise à jour");
				commitUpdateChange(change, updatedServiceByApprover, useApproverUpdates, approver);
				break;
				
			case MOVE:
				LoggingUtils.logDebug(logger, "Approbation d'un déplacement");
				commitMoveChange(change, approver);
				break;
				
			default:
				throw new UnsupportedOperationException("Opération non reconnue");
			}
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	@Override
	@Transactional(readOnly = false)
	public void commitAndReject(Change change, User approver) {
		try {
			LoggingUtils.logDebug(logger, "Changement refusé");
			if (change.getOperation() != Operation.CREATE) {
				LoggingUtils.logDebug(logger, "Supprime les changements liés au service");
				OrgUnit service = getServiceFromChange(change, false);
				// Detacher le service du changement
				service.setChanges(null);
				orgUnitDao.persist(service);
			}
			
			LoggingUtils.logDebug(logger, "Mise à jour de l'état de la tâche");
			// Si le changement est refuse, terminer la teche
			change.setLockedByUser(null);
			change.setStatus(TaskStatus.REJECTED);
			change.setApproverName(approver.getMatricule());
			taskDao.save(change);
			
			// Rechercher tous les utilisateurs e qui la teche est assignee
			// Equivalent de change.setApprovers(null); si la relation etait EAGER
			LoggingUtils.logDebug(logger, "Désassigne la tâche");
			unassignChange(change.getId());
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	/**
	 * 
	 * @param change
	 * @return
	 */
	private OrgUnit getServiceFromChange(Change change, boolean applyChanges) {
		// Recupere l'objet contenant les changements
		String xmlChanges = change.getObject();

		Serializer serializer = new Persister();
		UpdatesList updates = null;
		try {
			updates = serializer.read(UpdatesList.class, xmlChanges);
		} catch (Exception e) {
			throw new IllegalStateException(
					"Impossible de desérialiser les changements", e);
		}
		
		// Recupere le service e mettre e jour
		OrgUnit service = orgUnitDao.find(updates.getServiceId());

		if (applyChanges) {
			service.applySerializedChanges(xmlChanges);
		}
		
		return service;
	}

	/**
	 * 
	 * @param changeId
	 */
	private void unassignChange(Integer changeId) {
		// Rechercher tous les utilisateurs e qui la teche est assignee
		Change assignedChange = taskDao.find(changeId);
		Set<User> approversForTask = assignedChange.getApprovers();
		// supprimer les teches assignees aux autres approbateurs
		for (User approverForTask : approversForTask) {
			approverForTask.removeTask(assignedChange);
			userDao.persist(approverForTask);
		}
	}
	
	/*
	 * Valide un changement de deplacement de service
	 * 
	 * @param change
	 * @param approver
	 */
	private void commitMoveChange(Change change, User approver) {
		// Recupere l'objet
		String xmlChanges = change.getObject();

		Serializer serializer = new Persister();
		UpdatesList updates = null;
		try {
			updates = serializer.read(UpdatesList.class, xmlChanges);
		} catch (Exception e) {
			throw new IllegalStateException(
					"Impossible de desérialiser les changements", e);
		}

		// Recupere le service e mettre e jour
		OrgUnit service = orgUnitDao.find(updates.getServiceId());
		
		// Applique les mises e jour sauf celles concernant le deplacement
		service.applySerializedChanges(xmlChanges);
		
		// Positionnement du flag
		service.setDeplace(true);
		service.setModificationDate(new Date());

		// Detacher le service du changement
		service.setChanges(null);

		orgUnitDao.save(service);
		
		// Marque la teche terminee et supprime la teche des autres approbateurs
		change.setLockedByUser(null);
		change.setStatus(TaskStatus.APPROVED);
		change.setApproverName(approver.getMatricule());
		taskDao.save(change);
		
		// Rechercher tous les utilisateurs e qui la teche est assignee
		unassignChange(change.getId());
	}
	
	/*
	 * Valide un changement de mise e jour de service
	 * 
	 * @param change
	 * @param approver
	 */
	private void commitUpdateChange(Change change, OrgUnit updatedServiceByApprover, boolean useApproverUpdates, User approver) {
		OrgUnit service = null;
		
		// Recupere l'objet contenant les changements
		String xmlChanges = change.getObject();

		Serializer serializer = new Persister();
		UpdatesList updates = null;
		try {
			updates = serializer.read(UpdatesList.class, xmlChanges);
		} catch (Exception e) {
			throw new IllegalStateException(
					"Impossible de desérialiser les changements", e);
		}

		if (!useApproverUpdates) {
			// Recupere le service e mettre e jour
			service = orgUnitDao.find(updates.getServiceId());
			
			// Applique les mises e jour sauf celles concernant le deplacement
			service.applySerializedChanges(xmlChanges);
		} else {
			service = updatedServiceByApprover;
		}
		
		Integer newParentId = null;
		// Si le changement contient un deplacement depuis le demandeur
		// ou si un changement a eu lieu par le referent.
		// Traiter en premier lieu le referent
		if (service.getUpdates().containsUpdateOf(UpdateType.PARENT)) {
			// Chercher le nouveau pere
			Update parentUpdate = service.getUpdates().getUpdateOf(UpdateType.PARENT);
			newParentId = (Integer) parentUpdate.getAttributeUpdates().get("parentId");
		} else if (updates.containsUpdateOf(UpdateType.PARENT)) {
			// Chercher le nouveau pere
			Update parentUpdate = updates.getUpdateOf(UpdateType.PARENT);
			newParentId = (Integer) parentUpdate.getAttributeUpdates().get("parentId");
		}
		
		if (newParentId != null) {
			// Appliquer e toute la descendance le nouveau chemin
			orgUnitDao.moveHierarchy(service, newParentId);
		}
		
		// Positionnement du flag de modification
		if (service.getUpdates().containsUpdateOf(UpdateType.ADDRESS) ||
				updates.containsUpdateOf(UpdateType.ADDRESS)) {
			service.setModifAdresse(true);
		}
		if (service.getUpdates().containsUpdateOf(UpdateType.LABEL) ||
				updates.containsUpdateOf(UpdateType.LABEL)) {
			service.setLibModifie(true);
		}

		service.setModificationDate(new Date());
		
		// Detacher le service du changement
		service.setChanges(null);
		
		orgUnitDao.save(service);

		// Marque la teche terminee et supprime la teche des autres approbateurs
		change.setLockedByUser(null);
		change.setStatus(TaskStatus.APPROVED);
		change.setApproverName(approver.getMatricule());
		taskDao.save(change);
		
		// Rechercher tous les utilisateurs e qui la teche est assignee
		unassignChange(change.getId());
	}
	
	/*
	 * Valide un changement de suppression de service
	 *  
	 * @param change
	 * @param approver
	 */
	private void commitDeleteChange(Change change, User approver) {
		// Recupere l'objet contenant les changements
		String xmlChanges = change.getObject();

		Serializer serializer = new Persister();
		UpdatesList updates = null;
		try {
			updates = serializer.read(UpdatesList.class, xmlChanges);
		} catch (Exception e) {
			throw new IllegalStateException(
					"Impossible de desérialiser les changements", e);
		}
		
		// Recupere le service e mettre e jour
		OrgUnit deletedService = orgUnitDao.find(updates.getServiceId());
		
		// Applique les mises e jour
		deletedService.applySerializedChanges(xmlChanges);

		// Positionnement du flag
		deletedService.setSupprime(true);
		deletedService.setModificationDate(new Date());

		// Detacher le service du changement
		deletedService.setChanges(null);
		orgUnitDao.persist(deletedService);
		
		// Selectionner tous les fils ayant le meme chemin parent
		// orgUnitDao.deleteHierarchy(deletedService);
		
		// Marque la teche terminee et supprime la teche des autres approbateurs
		change.setLockedByUser(null);
		change.setStatus(TaskStatus.APPROVED);
		change.setApproverName(approver.getMatricule());
		taskDao.save(change);
		
		// Rechercher tous les utilisateurs e qui la teche est assignee
		unassignChange(change.getId());
	}
	
	/*
	 * Valide un changement de creation de service
	 *  
	 * @param change
	 * @param approver
	 */
	private void commitNewChange(Change change, OrgUnit updatedServiceByApprover, boolean useApproverUpdates, User approver) {
		OrgUnit newService = null;
		
		if (!useApproverUpdates) {
			// Recupere l'objet
			String xmlObject = change.getObject();
	
			Serializer serializer = new Persister();
			try {
				newService = serializer.read(OrgUnit.class, xmlObject);
			} catch (Exception e) {
				throw new IllegalStateException(
						"Impossible de desérialiser le service", e);
			}
			
			// Recheche du service parent e partir de son identifiant
			// Cet identifiant ne change pas meme s'il a ete deplace.
			Integer parentId = newService.getParentId();
			OrgUnit parent = orgUnitDao.find(parentId);
			newService.setParent(parent);
		} else {
			newService = updatedServiceByApprover;
		}
		
		// Positionnement du flag
		newService.setCree(true);
		newService.setModificationDate(new Date());
		
		// Appel au DAO; Avec les services pas je n'ai pas 
		// encore reussi e faire partager les services entre
		// eux e la transaction en cours
		orgUnitDao.save(newService);

		// Marque la teche terminee et supprime la teche des autres approbateurs
		change.setLockedByUser(null);
		change.setStatus(TaskStatus.APPROVED);
		change.setApproverName(approver.getMatricule());
		taskDao.save(change);

		// Rechercher tous les utilisateurs e qui la teche est assignee
		unassignChange(change.getId());
	}

	@Override
	@Transactional(readOnly = false)
	public void revert(Change change, User approver) {
		try {
			// Positionne le statut du changement
			change.setStatus(TaskStatus.AVAILABLE);
			// Supprime le nom de l'approbateur
			change.setLockedByUser(null);
			taskDao.save(change);
		} catch (PersistenceException e) {
			throw new BusinessException(e.getMessage(), e);
		}
	}

	@Override
	public List<Change> findMyTasks(QueryParam<Change> queryParameters) {
		String matricule = SecurityHelper.getAuthentication().getName();
		
		// Recherche les teches de la direction
		return taskDao.findMyTasks(queryParameters, matricule);
	}

	@Override
	public Integer countMyTasks(QueryParam<Change> queryParameters) {
		String matricule = SecurityHelper.getAuthentication().getName();
		return taskDao.countMyTasks(matricule);
	}
	
	@Override
	public List<Change> findMyGroupTasks(QueryParam<Change> queryParameters) {
		String matricule = SecurityHelper.getAuthentication().getName();
		
		List<TaskStatus> inStatus = setTaskStatus(queryParameters);
		List<Operation> inOperation = setOperation(queryParameters);
		
		// Recherche des teches de ma direction
		return taskDao.findMyGroupTasks(queryParameters, matricule, inStatus, inOperation);
	}

	@Override
	public Integer countMyGroupTasks(QueryParam<Change> queryParameters) {
		String matricule = SecurityHelper.getAuthentication().getName();

		List<TaskStatus> inStatus = setTaskStatus(queryParameters);
		List<Operation> inOperation = setOperation(queryParameters);

		// Compter les teches de ma direction
		return taskDao.countMyGroupTasks(matricule, inStatus, inOperation);
	}

	/**
	 * @param queryParameters
	 * @return
	 */
	private List<TaskStatus> setTaskStatus(QueryParam<Change> queryParameters) {
		List<TaskStatus> inStatus = new ArrayList<TaskStatus>();
		Change example = queryParameters.getCriteriaExample();
		
		if (example == null || example.getStatus() == null) {
			inStatus.add(TaskStatus.AVAILABLE);
			inStatus.add(TaskStatus.RESERVED);
		} else {
			inStatus.add(example.getStatus());
		}
		
		return inStatus;
	}

	/**
	 * @param queryParameters
	 * @return
	 */
	private List<Operation> setOperation(QueryParam<Change> queryParameters) {
		List<Operation> inOperation = null;
		Change example = queryParameters.getCriteriaExample();
		
		if (example == null || example.getOperation() == null) {
			inOperation = Arrays.asList(Operation.values());
		} else {
			inOperation = new ArrayList<Operation>();
			inOperation.add(example.getOperation());
		}
		
		return inOperation;
	}

}
